/*******************************************************************************
* Copyright © 2012-2015 eBay Software Foundation
* This program is dual licensed under the MIT and Apache 2.0 licenses.
* Please see LICENSE for more information.
*******************************************************************************/
package com.ebay.jetstream.http.netty.server;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.timeout.IdleStateEvent;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ebay.jetstream.counter.LongCounter;
import com.ebay.jetstream.counter.LongEWMACounter;
import com.ebay.jetstream.messaging.MessageServiceTimer;
/**
*
* @author shmurthy@ebay.com (shmurthy@ebay.com)
*
*/
@Sharable
public class HttpRequestHandler extends ChannelDuplexHandler {
private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.http.netty.server");
/**
* @return the getTotalRcvCount
*/
public long getRcvCountPerSec() {
return m_perSecRcvCount.get();
}
/**
* @return the getTotalRcvCount
*/
public long getTotalRcvCount() {
return m_totalRcvCount.get();
}
/**
* @return the get total content length
*/
public long getTotalContentLength() {
return m_totalContentLength.get();
}
private HttpServer m_server = null;
private LongEWMACounter m_perSecRcvCount = new LongEWMACounter(60, MessageServiceTimer
.sInstance().getTimer());
private LongCounter m_totalRcvCount = new LongCounter();
private LongCounter m_totalContentLength = new LongCounter();
public HttpRequestHandler(HttpServer ec) {
m_server = ec;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (LOGGER.isDebugEnabled()) {
debug("Rcvr Session created to host - " + ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
}
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (LOGGER.isDebugEnabled()) {
debug("EventProducerSessionHandler -> session closed to host - "
+ ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
}
super.channelInactive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
m_perSecRcvCount.increment();
if (m_totalRcvCount.addAndGet(1) < 0)
m_totalRcvCount.increment();
processHttpRequest((HttpRequest) msg, ctx);
}
private void debug(String message) {
LOGGER.debug(message);
}
private void debugHeadersAndCookies(HttpRequest request) {
StringBuilder headersandaccokies = new StringBuilder();
// echo the header for now
for (Map.Entry<String, String> h : request.headers()) {
headersandaccokies.append("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n");
}
headersandaccokies.append("\r\n");
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
Map<String, List<String>> params = queryStringDecoder.parameters();
if (!params.isEmpty()) {
for (Entry<String, List<String>> p : params.entrySet()) {
String key = p.getKey();
List<String> vals = p.getValue();
for (String val : vals) {
headersandaccokies.append("PARAM: " + key + " = " + val + "\r\n");
}
}
headersandaccokies.append("\r\n");
}
debug(headersandaccokies.toString());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable ee) throws Exception {
String message = "Exception Caught while communicating to ";
try {
message += ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName();
message += ee.getCause();
printInfo(message);
} catch (Throwable t) {
}
printInfo("EventProducerSessionHandler -> exceptionCaught" + ee.toString());
super.exceptionCaught(ctx, ee);
}
/**
* @param message
*/
private void printInfo(String message) {
LOGGER.info( message);
}
private void processHttpRequest(HttpRequest message, ChannelHandlerContext ctx) throws Exception {
if (LOGGER.isDebugEnabled()) {
debugHeadersAndCookies(message);
}
// Expect: 100-continue should be handled by HttpObjectAggregator.
ByteBuf buf = ((FullHttpMessage) message).content();
m_totalContentLength.addAndGet(buf.readableBytes());
m_server.processHttpRequest(message, ctx.channel());
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
ctx.close();
}
}
}